/*
 *  Copyright 2008-2010 www.jrails.org
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.rails.extjs.json;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.rails.extjs.AbsBase;
import org.rails.extjs.javascript.Script;

@SuppressWarnings("serial")
public class Json extends HashMap<String,Object> {

	public static boolean debug = false;
	
	public Json(){
		super();
	}
	
	public Json(String key,Object value){
		super();
		put(key,value);
	}
	
	public Json(Map<String,Object> m){
		super(m);
	}
	
	protected String escapeString(String value){
		if (value == null)
			return "null";
		
		final StringBuffer sbf = new StringBuffer("\"");
		sbf.append(value.replace("\\", "\\u005C").replace("\"", "\\\"").replace("\n",
				"\\n").replace("\r", "\\r"));
		sbf.append("\"");
		return sbf.toString();
	}
	
	protected String escapeChar(Character value){
		if (value == null)
			return "null";
		else
			return escapeString(value.toString());
	}
	
	protected String escapeJson(Json value){
		if (value == null)
			return "null";
		else
			return value.createJson();
	}
	
	protected String escapeArray(Object[] value){
		if (value == null)
			return "null";
		
		return escapeValue(Arrays.asList(value));
	}
	
	protected String escapeList(List<Object> value){
		if (value == null)
			return "null";
		
		final int size = value.size();
		final StringBuffer s = new StringBuffer("[");
		s.append(debugSequence());
		for (int i = 0; i < size; i++) {
			s.append(escapeValue(value.get(i)));
			if (i < size - 1)
				s.append(",");

			s.append(debugSequence());
		}
		s.append("]");
		return s.toString();
	}
	
	protected String escapeHash(Map<String,Object> value){
		if (value == null)
			return "null";
		
		final StringBuffer s = new StringBuffer("{");
		s.append(debugSequence());
		List<String> keys = getKeys(value);
		int size = keys.size();
		for (int i = 0; i < size; i++) {
			String key = keys.get(i);
			Object v = value.get(key);
			s.append(escape(key,v));
			if (i < size - 1) 
				s.append(",");
			
			s.append(debugSequence());
		}
		s.append("}");
		return s.toString();
	}
	
	protected String escapeAbsBase(AbsBase value){
		if(value == null)
			return "null";
		else
			return value.getScript();	
	}
	
	protected String escapeScript(Script value){
		if(value == null)
			return "null";
		else
			return value.getScript();	
	}
	
	protected String escapeNumber(Number value){
		if(value == null)
			return "null";
		else
			return value.toString();		
	}
	
	protected String escapeBoolean(Boolean value){
		if(value == null)
			return "null";
		else
			return value.toString();		
	}
	
	protected String escapeOther(Object value){
		if(value == null)
			return "null";
		else
			return escapeString(value.toString());		
	}
	
	public static String escape(Object value){
		return new Json().escapeValue(value);
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected String escapeValue(Object value){
		if(value == null)
			return "null";
		
		if(value instanceof String){
			return escapeString(value.toString());
		}else if(value instanceof Character){
			return escapeChar((Character)value);
		}else if(value instanceof Number){
			return escapeNumber((Number)value);
		}else if(value instanceof Boolean){
			return escapeBoolean((Boolean)value);
		}else if(value instanceof AbsBase){
			return escapeAbsBase((AbsBase)value);
		}else if(value instanceof Script){
			return escapeScript((Script)value);
		}else if(value instanceof Map){
			return escapeHash((Map)value);
		}else if(value instanceof List){
			return escapeList((List)value);
		}else if(value instanceof Object[]){
			return escapeArray((Object[])value);
		}else if(value instanceof Json){
			return escapeJson((Json)value);
		}else{
			return escapeOther(value);
		}
	}
	
	protected String escapeKey(String key){
		return escapeString(key);
	}
	
	protected String escape(String key, Object value) {
		return escapeKey(key) + ":" + escapeValue(value);
	}
	
	public List<String> getKeys() {
		return getKeys(this);
	}

	public static List<String> getKeys(Map<String, Object> m) {
		final List<String> keys = new ArrayList<String>();
		for (Iterator<String> iterator = m.keySet().iterator(); iterator
				.hasNext();) {
			keys.add(iterator.next());
		}
		return keys;
	}
	
	public String createJson() {
		final StringBuffer s = new StringBuffer("{");
		s.append(debugSequence());
		final List<String> keys = getKeys();
		int size = keys.size();
		for (int i = 0; i < size; i++) {
			String key = keys.get(i);
			Object value = this.get(key);
			s.append(escape(key,value));
			if (i < size - 1) 
				s.append(",");
			
			s.append(debugSequence());
		}
		s.append("}");
		return s.toString();
	}
	
	public Json append(String key,Object value){
		put(key, value);
		return this;
	}
	
	public Json append(Map<String,Object> m){
		putAll(m);
		return this;
	}
	
	protected static String debugSequence() {
		if(debug)
			return "\n";
		else
			return "";
	}
	
	public static void main(String[] args) {
		Json json = new Json(){
			
			@Override
			protected String escapeHash(Map<String,Object> map){
				System.out.println(map);
				return super.escapeHash(map);
			}
		};
		json.put("total",3);
		
		List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("id",1);
		map.put("name","jack ye");
		list.add(map);
		 map = new HashMap<String, Object>();
			map.put("id",2);
			map.put("name","tengjun");
			list.add(map);
		json.put("data",list);
		System.out.println(json.createJson());
	}
	
}
